﻿using System;
using System.Net.Sockets;

namespace zTalk.Network
{
    public class SocketBase
    {
        public const int BUFFER_SIZE = 1024;

        public Socket Socket;
        protected byte[] buffer;
        protected PacketReader reader;

        public SocketBase()
        {
            buffer = new byte[BUFFER_SIZE];
            reader = new PacketReader();
        }

        protected virtual void startReceive()
        {
            try
            {
                Socket.BeginReceive(buffer, 0, BUFFER_SIZE, 
                    SocketFlags.None, new AsyncCallback(onReceive), Socket);
            }
            catch (ObjectDisposedException)
            {
                Close();
            }
            catch (SocketException)
            {
                Close();
            }
        }

        protected virtual void onReceive(IAsyncResult ar)
        {
            Socket s = (Socket)ar.AsyncState;

            try
            {
                int count = s.EndReceive(ar);

                byte[] data = new byte[count];
                Array.Copy(buffer, 0, data, 0, count);

                reader.ReadData(this, data);

                if (count > 0)
                {
                    s.BeginReceive(buffer, 0, BUFFER_SIZE, 
                        SocketFlags.None, new AsyncCallback(onReceive), s);
                }
                else
                {
                    Close();
                }
            }
            catch (ObjectDisposedException)
            {
                Close();
            }
            catch (SocketException)
            {
                Close();
            }
        }

        public virtual void SendPacket(Packet packet)
        {
            byte[] data = new byte[packet.Length + Packet.HEADER_LENGTH];

            data[0] = (byte)packet.Opcode;

            byte[] temp = BitConverter.GetBytes(packet.Length);
            data[1] = temp[0];
            data[2] = temp[1];
            data[3] = temp[2];
            data[4] = temp[3];

            Array.Copy(packet.Data, 0, data, 
                Packet.HEADER_LENGTH, packet.Length);

            try
            {
                Socket.BeginSend(data, 0, data.Length, 
                    SocketFlags.None, new AsyncCallback(onSend), Socket);
            }
            catch (ObjectDisposedException)
            {
                Close();
            }
            catch (SocketException)
            {
                Close();
            }
            catch (NullReferenceException)
            {
                //MessageBox.Show("Connection to server lost");
            }
        }

        protected virtual void onSend(IAsyncResult ar)
        {
            Socket s = (Socket)ar.AsyncState;

            try
            {
                s.EndSend(ar);
            }
            catch (ObjectDisposedException)
            {
                Close();
            }
            catch (SocketException)
            {
                Close();
            }
        }

        public virtual void Close()
        {
            try
            {
                Socket.Shutdown(SocketShutdown.Both);
                Socket.Close();
                Socket = null;
            }
            catch (Exception)
            {
            }
        }
    }
}
